package co.edu.usbcali.ai.ga;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import com.cgarias.utils.Random;

/**
 * Esta clase define un tipo de gen, esta compuesta por un nombre y una lista de valores,
 * lo cuales reprensentan los valores posibles que pueden tomar un gen de este tipo.
 * @author cgarias
 *
 * @param <T> Es el tipo de objeto de los elementos del GeneType
 */

public class GeneType<T extends Serializable> implements Serializable{

	//TODO Permitir que se puedan usar rangos en lugar de categorias
	private String name;
	private List<T> domain;
	private int type=1;
	private float maxValue=0;
	private float minValue=0;
	public static final int CONTINUOS=0;
	public static final int DISCRETE=1;
	
	/* **********************************************
	 * CONSTRUCTORS
	 * **********************************************/
	public GeneType() {
		domain=new ArrayList<T>();
		this.name="noName";
	}

	public GeneType(List<T> domain, String type) {
		super();
		this.domain = domain;
		this.name = type;
	}

	/* **********************************************
	 * 
	 * **********************************************/
	//TODO Se debería devolver una nueva instancia del objeto referenciado en la lista
	//en lugar de devolver el mismo objeto
	/**
	 * Retorna uno de los posibles valores de este tipo de gen. Este método normalmente se
	 * invoca durante la creación de genes aleatorios.
	 */
	public T getRandomObject(){
		return getDomain().get(Random.random(getDomain().size()));
	}

	public float getRandomValue(){
		return Random.random(minValue,maxValue);
	}
	
	/**
	 * Retorna un objeto dento del conjunto de valores posibles definidos por el objeto.
	 * Si es de tipo CONTINUOS retorna un valor dentro del rango, si es de tipo DISCRETE
	 * retorna uno de los elementos del dominio.
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public T getRandom(){
		if (isDiscrete()){
			return getRandomObject();
		}else if (isContinuos()){
			return (T)Float.valueOf(getRandomValue());
		}
		return null;
	}
	/* **********************************************
	 * GETTERS AND SETTERS
	 * **********************************************/

	public List<T> getDomain() {
		return domain;
	}
	public void setDomain(List<T> domain) {
		this.domain = domain;
	}
	public String getName() {
		return name;
	}
	public void setName(String p_name) {
		this.name = p_name;
	}
	
	
	public int getType() {
		return type;
	}

	public void setType(int type) {
		this.type = type;
	}
	public float getMaxValue() {
		return maxValue;
	}

	public void setMaxValue(float maxValue) {
		this.maxValue = maxValue;
	}

	public float getMinValue() {
		return minValue;
	}

	public void setMinValue(float minValue) {
		this.minValue = minValue;
	}

	public boolean isContinuos(){
		return type==CONTINUOS;
	}
	public boolean isDiscrete(){
		return type==DISCRETE;
	}

	//TODO Validar que el metodo equals entre listas funcione
	@SuppressWarnings("unchecked")
	public boolean equals(Object p_geneType){
		if (p_geneType==null){
			return false;
		}else{
			//TODO Incluir una captura de la excepcion CastClass cuando el parametro no se pueda convertir
			GeneType<T> other=(GeneType<T>)p_geneType;
			return (getName().equals(other.getName())&&getDomain().equals(other.getDomain()));
		}
	}
	

}
